package nl.ViewerServer.ServerManager.DataManager.PipelineObjects;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import nl.ViewerServer.Exceptions.ParameterException;
import nl.ViewerServer.ServerManager.DataManager.Objects.JavaPythonUtil;
import nl.ViewerServer.ServerManager.DataManager.Objects.Parameter;

import org.apache.log4j.Logger;

public class PipelineObject {
   private static Logger                log        = Logger.getLogger( PipelineObject.class );
   public final int                     objectId;
   public String                        name;
   protected String                     script     = null;
   protected HashMap<String, Parameter> parameters = new HashMap<String, Parameter>();

   public static enum ObjectType {
      SOURCE, VISUALIZATION, OUTPUT
   };

   public PipelineObject( int objectId, String name, String script ) {
      super();
      this.objectId = objectId;
      this.name = name;
      if( script != null ) {
         this.script = loadScript( script );
      }
   }

   public void loadScript( File file ) throws FileNotFoundException, IOException {
      BufferedReader br = new BufferedReader( new FileReader( file ) );
      String line;

      StringBuffer output = new StringBuffer();
      while( ( line = br.readLine() ) != null ) {

         if( !line.trim().startsWith( "#" ) ) {
            output.append( line + "\n" );
         }
      }
      br.close();

      script = output.toString();
   }

   public String loadScript( String script ) {
      String[] scriptLines = script.split( "[\\r]?\\n" );

      StringBuffer output = new StringBuffer();
      for( String s : scriptLines ) {
         if( !s.trim().startsWith( "#" ) ) {
            output.append( s + "\n" );
         }
      }

      return output.toString();
   }

   public String parseScript() {
      if( script == null || script.length() == 0 )
         return null;
      String output = new String( script );
      Pattern insertParameter = Pattern.compile( "##\\[(.*?)\\]", Pattern.MULTILINE );
      Matcher m = insertParameter.matcher( output );
      while( m.find() ) {
         String replacementValue = null;

         Object replacementObject = null;

         replacementObject = parameters.get( m.group( 1 ) );
         if( replacementObject != null ) {
            replacementValue = ( (Parameter)replacementObject ).getPythonRepresentation();
         }
         if( replacementValue != null ) {
            output = m.replaceAll( replacementValue );
         } else {
            log.warn( "Warning: Script parameter '" + m.group( 1 ) + "' is not defined in this pipeline object" );
         }
      }
      return output;
   }

   public Parameter getParameter( String name ) {
      return parameters.get( name );
   }

   public Parameter getParameter( int parameterId ) {
      Iterator<Parameter> iter = parameters.values().iterator();
      while( iter.hasNext() ) {
         Parameter p = iter.next();
         if( p.parameterId == parameterId )
            return p;
      }
      return null;
   }

   public HashMap<String, Parameter> getParameters() {
      return parameters;
   }

   public void addParameter( int parameterId, String parameterName, String parameterType, boolean isTuple,
         int parameterLength, String defaultPythonValue, boolean isUserViewable, boolean isUserModifiable,
         String parameterGetter, String parameterSetter, String setterType, boolean requiredForSynchronization )
         throws ParameterException {

      Parameter p = Parameter.newInstance( parameterId, parameterName, parameterType, isTuple, parameterLength,
            defaultPythonValue, isUserViewable, isUserModifiable, parameterGetter, parameterSetter, setterType,
            requiredForSynchronization );

      parameters.put( p.parameterName, p );

      log.debug( "addParameter: Parameter added for pipelineObject '" + name + "': " + parameterName + ", setter type: " + p.getSetterType().name() );

   }

   public void addParameterValue( int parameterId, String pythonParameterValue ) throws ParameterException {

      Parameter p = getParameter( parameterId );
      if( p == null ) {
         // this indicates incorrect database data
         log.warn( "addParameterValue: Parameter '" + parameterId + "' was not found for pipelineObject '" + this.name + "'" );
         return;
      }

      // convert python parameter to java object
      Object javaObject = JavaPythonUtil.parsePythonObjectToJavaObject( pythonParameterValue );
      // convert java object to correct parameter value
      Object parameterValue = JavaPythonUtil.convertJavaObjectToParameter( javaObject, p.getParameterType(),
            p.getParameterLength() );

      p.setParameterValue( parameterValue );
      p.setUserModifiable( false ); // force it to non-modifiable

      log.debug( "A (fixed) parameter value was set for pipelineObject: " + this.objectId + ", for parameter " + p.parameterName + ": " + p.getPythonRepresentation() );
   }

   public String getObjectType() {
      if( this instanceof Pipeline ) {
         return ObjectType.VISUALIZATION.toString();
      } else if( this instanceof PipelineOutput ) {
         return ObjectType.OUTPUT.toString();
      } else if( this instanceof PipelineSource ) {
         return ObjectType.SOURCE.toString();
      }
      return null;
   }

}
